﻿using System;
using System.Collections.Generic;
using XAMSDK.Types;

namespace XAMSDK
{
    /// <summary>
    /// Used by callback methods to obtain information on the Async operation
    /// </summary>
    public class XAsyncObject: IDisposable
    {
        static internal List<XAsyncObject> objects = new List<XAsyncObject>();
        XAsyncHandle xah;
        XAsyncType op;
        XString fieldName = new XString();
        XAMHandle xamHandle;

        #region Constructors
        /// <summary>
        /// Internal constructor
        /// </summary>
        /// <param name="h">the XAsyncHandle of the operation</param>
        internal XAsyncObject(XAsyncHandle h, XAsyncType t)
        {
            lock (objects)
            {
                xah = h;
                op = t;

                objects.Add(this);
            }
        }

        internal XAsyncObject(XAsyncHandle h, XAsyncType t, XAMHandle handle, XString fn)
            :this(h, t)
        {
            xamHandle = handle;
            fieldName = fn;
        }
        #endregion

        #region IDisposable
        protected bool disposed = false;

        // IDisposable
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Dispose(bool disposing)
        {
            if (!disposed)
            {
                disposed = true;
                // Cleanup any unmanaged objects - the XAsynncHandle
                Close();
            }
        }
        #endregion

        ~XAsyncObject()
        {
            Dispose(false);
        }

        #region Properties
        /// <summary>
        /// The XUID associated with the instance. Only valid if the operation was an XSet Commit or Copy.
        /// </summary>

        public XUID XUID
        {
            get
            {
                XUID xuid = new XUID();

                int retval = Imports.XAsync_GetXUID(xah, xuid);
                Helpers.CheckAndThrow(retval, "XAsync_GetXUID");             
                
                return xuid;
            }
        }
        /// <summary>
        /// The XSet associated with the instance. Only valid if the operation was an XSet Commit or Copy.
        /// </summary>
        public XAMSDK.XSet XSet
        {
            get
            {
                XSetHandle x;
                int retval = Imports.XAsync_GetXSet(xah, out x);
                Helpers.CheckAndThrow(retval, "XAsync_GetXSet");

                return XSet.Get(x); 
            }
        }

        /// <summary>
        /// The XStream associated with the instance. Only valid if the operation was an XStream Read, Write or Close.
        /// </summary>
        public XStream Stream
        {
            get
            {
                XStreamHandle s;

                int retval = Imports.XAsync_GetXStream(xah, out s);
                Helpers.CheckAndThrow(retval, "XAsync_GetXStream");

                XStream xs = XStream.Get(s);

                if (xs == null)
                {
                    // XStream was being opened by async call
                    if (this.op == XAsyncType.XStreamOpenOutput)
                        xs = new XStream(xamHandle, fieldName, s, Constants.OUTPUT_STREAM);
                    else
                        xs = new XStream(xamHandle, fieldName, s, Constants.INPUT_STREAM);
                }

                return xs;
            }
        }

        /// <summary>
        /// The number of bytes read from the XStream. Only valid if the operation was an XStream Read.
        /// </summary>
        public long BytesRead
        {
            get
            {
                long bytes = 0;

                int retval = Imports.XAsync_GetBytesRead(xah, out bytes);
                Helpers.CheckAndThrow(retval, "XAsync_GetBytesRead");

                return bytes;
            }
        }

        /// <summary>
        /// The number of bytes written from the XStream. Only valid if the operation was an XStream Write.
        /// </summary>
        public long BytesWritten
        {
            get
            {
                long bytes = 0;

                int retval = Imports.XAsync_GetBytesWritten(xah, out bytes);
                Helpers.CheckAndThrow(retval, "XAsync_GetBytesWritten");

                return bytes;
            }
        }

        /// <summary>
        /// The status of the operation.
        /// </summary>
        public int Status
        {
            get
            {
                int status = 0;

                int retval = Imports.XAsync_GetStatus(xah, out status);
                Helpers.CheckAndThrow(retval, "XAsync_GetStatus");

                return status;
            }
        }

        /// <summary>
        /// Has the operation completed?
        /// </summary>
        public bool Completed
        {
            get
            {
                bool completed = false;

                int retval = Imports.XAsync_IsComplete(xah, out completed);
                Helpers.CheckAndThrow(retval, "XAsync_IsComplete");

                return completed;
            }
        }

        /// <summary>
        /// The used defined ID of the operation.
        /// </summary>
        public long ID
        {
            get
            {
                long id;

                int retval = Imports.XAsync_GetXOPID(xah, out id);
                Helpers.CheckAndThrow(retval, "XAsync_GetXOPID");

                return id;
            }
        }

        #endregion
        #region Methods

        /// <summary>
        /// Explicitly Close theAsyncObject. Not really needed as this will be done by Dispose
        /// </summary>
        protected void Close()
        {
            lock (objects)
            {
                int retval = Imports.XAsync_Close(xah);
                Helpers.CheckAndThrow(retval, "XAsync_Close");

                objects.Remove(this);
            }
        }

        /// <summary>
        /// Halt the processing of the XAsync Operation associated with this instance.
        /// </summary>
        public void Halt()
        {
            lock (objects)
            {
                int retval = Imports.XAsync_Halt(xah);
                Helpers.CheckAndThrow(retval, "XAsync_Halt");

                objects.Remove(this);
            }
        }

        /// <summary>
        /// Find the XAsyncObject associated with an XAsyncHandle.
        /// </summary>
        /// <param name="h"></param>
        /// <returns></returns>
        public static XAsyncObject Get(XAsyncHandle h)
        {
            XAsyncObject ao;
            
            lock(objects)
                ao = objects.Find(delegate(XAsyncObject o) { return o.xah == h; } );

            return ao;
        }
        #endregion

        #region Implicit Casts
        /// <summary>
        /// For convenience only - an implicit cast of an XAsyncHandle to an XAsyncObject. Uses the Get static method to retrieve it.
        /// </summary>
        /// <param name="h"></param>
        /// <returns></returns>
        public static implicit operator XAsyncObject(XAsyncHandle h)
        {
            return Get(h);
        }
        #endregion
    }
}